import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class EdgeDevice {
    private String deviceId;
    private String location;
    private boolean isSchoolZone;
    private boolean useFixedTiming;
    private Queue<TrafficData> localDataQueue;
    private ConcurrentHashMap<Integer, Integer> signalTiming;
    private DefaultListModel<String> logModel;
    private List<Vehicle> vehicles;
    private Random random = new Random();

    public enum LightState {
        RED, GREEN, YELLOW
    }

    private LightState[] lightStates;
    private int[] lightTimers;
    private int[] laneVehicles;
    private final int yellowLightDuration = 3;
    private int minGreenTime;
    private int maxGreenTime;

    private int position_1_next_new = 0;
    private int position_2_next_new = 0;
    private int position_3_next_new = 0;
    private int position_4_next_new = 0;
    private int gap_1_vehicle = 40;
    private int gap_2_vehicle = 40;
    private int gap_3_vehicle = 40;
    private int gap_4_vehicle = 40;

    private int vehicle_id = 0;

    // Optimization fields (only used for dynamic timing)
    private boolean[] hasVehiclePassed = new boolean[2];
    private ScheduledExecutorService directionTimer;
    private int[] directionIdleTime = new int[2];
    private boolean[] directionActive = new boolean[2];

    public EdgeDevice(String deviceId, String location, boolean isSchoolZone, boolean useFixedTiming, DefaultListModel<String> logModel) {
        this.deviceId = deviceId;
        this.location = location;
        this.isSchoolZone = isSchoolZone;
        this.useFixedTiming = useFixedTiming;
        this.localDataQueue = new ConcurrentLinkedQueue<>();
        this.signalTiming = new ConcurrentHashMap<>();
        this.logModel = logModel;
        this.vehicles = Collections.synchronizedList(new ArrayList<>());

        if (useFixedTiming) {
            // 固定时长配置（40秒绿灯，40秒红灯）
            this.minGreenTime = 40;
            this.maxGreenTime = 40;
            int fixedGreenTime = 40;
            int fixedRedTime = 40;

            this.lightStates = new LightState[]{LightState.RED, LightState.GREEN};
            this.lightTimers = new int[]{fixedRedTime, fixedGreenTime};

            signalTiming.put(1, fixedGreenTime);
            signalTiming.put(2, fixedGreenTime);
            signalTiming.put(3, fixedGreenTime);
            signalTiming.put(4, fixedGreenTime);
        } else {
            // 动态优化配置
            if (isSchoolZone) {
                minGreenTime = 15;
                maxGreenTime = 40;
            } else {
                minGreenTime = 15;
                maxGreenTime = 50;
            }
            int initTime = isSchoolZone ? 25 : 40;
            this.lightStates = new LightState[]{LightState.RED, LightState.GREEN};
            this.lightTimers = new int[]{initTime + 3, initTime};

            signalTiming.put(1, initTime);
            signalTiming.put(2, initTime);
            signalTiming.put(3, initTime);
            signalTiming.put(4, initTime);

            this.directionTimer = Executors.newScheduledThreadPool(2);
        }

        this.laneVehicles = new int[]{0, 0, 0, 0};
        Arrays.fill(hasVehiclePassed, false);
        Arrays.fill(directionIdleTime, 0);
        Arrays.fill(directionActive, false);
    }

    public void start() {
        initializeVehicles();
        new Thread(this::collectData).start();
        new Thread(this::processLocally).start();
        if (!useFixedTiming) {
            startDirectionMonitoring();
        }
    }

    private void startDirectionMonitoring() {
        directionTimer.scheduleAtFixedRate(() -> {
            for (int i = 0; i < 2; i++) {
                if (directionActive[i] && !hasVehiclePassed[i]) {
                    directionIdleTime[i]++;
                    if (directionIdleTime[i] >= 3) {
                        synchronized (lightTimers) {
                            if (lightStates[i] == LightState.GREEN && lightTimers[i] > 3) {
                                lightTimers[i] -= 3;
                                log(time() + " - " + deviceId + ": 方向" + (i + 1) + " 无车辆通过，加速绿灯计时");
                            }
                        }
                        directionIdleTime[i] = 0;
                    }
                }
            }
        }, 1, 1, TimeUnit.SECONDS);
    }

    private void initializeVehicles() {
        int s_1 = 0;
        int s_2 = 0;
        int s_3 = 0;
        int s_4 = 0;
        for (int i = 0; i < 5; i++) {
            Vehicle pre_1 = getPreVehicle(1);
            Vehicle pre_2 = getPreVehicle(2);
            Vehicle pre_3 = getPreVehicle(3);
            Vehicle pre_4 = getPreVehicle(4);
            vehicles.add(new Vehicle(++vehicle_id, 1, s_1, pre_1));
            laneVehicles[0]++;
            vehicles.add(new Vehicle(++vehicle_id, 2, s_2, pre_2));
            laneVehicles[1]++;
            vehicles.add(new Vehicle(++vehicle_id, 3, s_3, pre_3));
            laneVehicles[2]++;
            vehicles.add(new Vehicle(++vehicle_id, 4, s_4, pre_4));
            laneVehicles[3]++;
            s_1 -= gap_1_vehicle;
            s_2 -= gap_2_vehicle;
            s_3 -= gap_1_vehicle;
            s_4 -= gap_2_vehicle;
        }
        position_1_next_new = s_1;
        position_2_next_new = s_2;
        position_3_next_new = s_3;
        position_4_next_new = s_4;
    }

    private void collectData() {
        int count = 0;
        while (!Thread.currentThread().isInterrupted()) {
            try {
                count++;
                synchronized (vehicles) {
                    if (count == 9) {
                        count = 0;
                        for (int i = 0; i < 5; i++) {
                            vehicles.add(new Vehicle(++vehicle_id, 1, position_1_next_new, getPreVehicle(1)));
                            laneVehicles[0]++;
                            vehicles.add(new Vehicle(++vehicle_id, 2, position_2_next_new, getPreVehicle(2)));
                            laneVehicles[1]++;
                            vehicles.add(new Vehicle(++vehicle_id, 3, position_3_next_new, getPreVehicle(3)));
                            laneVehicles[2]++;
                            vehicles.add(new Vehicle(++vehicle_id, 4, position_4_next_new, getPreVehicle(4)));
                            laneVehicles[3]++;
                            position_1_next_new -= gap_1_vehicle;
                            position_2_next_new -= gap_2_vehicle;
                            position_3_next_new -= gap_3_vehicle;
                            position_4_next_new -= gap_4_vehicle;
                        }
                    }
                }

                if (!useFixedTiming) {
                    TrafficData lane1 = new TrafficData(deviceId, 1, laneVehicles[0], 16);
                    TrafficData lane2 = new TrafficData(deviceId, 2, laneVehicles[1], 16);
                    TrafficData lane3 = new TrafficData(deviceId, 3, laneVehicles[2], 16);
                    TrafficData lane4 = new TrafficData(deviceId, 4, laneVehicles[3], 16);

                    localDataQueue.add(lane1);
                    localDataQueue.add(lane2);
                    localDataQueue.add(lane3);
                    localDataQueue.add(lane4);

                    log(time() + " - " + deviceId + ": 采集数据 - 车道1:" + laneVehicles[0] +
                            "辆, 车道2:" + laneVehicles[1] + "辆,车道3:" + laneVehicles[2] + "辆, 车道4:"
                            + laneVehicles[3] + "辆");
                }

                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private void processLocally() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                if (!useFixedTiming && !localDataQueue.isEmpty()) {
                    adjustTimingBasedOnTraffic();
                }
                updateTrafficLights();
                updateVehicles();
                if (!useFixedTiming) {
                    checkVehiclePassage();
                }
                TimeUnit.MILLISECONDS.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private void checkVehiclePassage() {
        synchronized (vehicles) {
            Arrays.fill(hasVehiclePassed, false);

            for (Vehicle vehicle : vehicles) {
                if (vehicle.getLaneId() == 1 || vehicle.getLaneId() == 3) {
                    if (vehicle.getY() > 200 && vehicle.getY() < 300) {
                        hasVehiclePassed[0] = true;
                        directionIdleTime[0] = 0;
                    }
                } else if (vehicle.getLaneId() == 2 || vehicle.getLaneId() == 4) {
                    if (vehicle.getX() > 150 && vehicle.getX() < 250) {
                        hasVehiclePassed[1] = true;
                        directionIdleTime[1] = 0;
                    }
                }
            }
        }
    }

    private void adjustTimingBasedOnTraffic() {
        TrafficData data = localDataQueue.poll();
        if (data != null) {
            int count = 0;
            synchronized (vehicles) {
                if (data.getLaneId() == 1) {
                    for (Vehicle vehicle : this.vehicles) {
                        if (vehicle.getLaneId() == 1 && vehicle.getY() < 185)
                            count++;
                    }
                } else if (data.getLaneId() == 2) {
                    for (Vehicle vehicle : this.vehicles) {
                        if (vehicle.getLaneId() == 2 && vehicle.getX() < 135)
                            count++;
                    }
                } else if (data.getLaneId() == 3) {
                    for (Vehicle vehicle : this.vehicles) {
                        if (vehicle.getLaneId() == 3 && vehicle.getY() > 285)
                            count++;
                    }
                } else if (data.getLaneId() == 4) {
                    for (Vehicle vehicle : this.vehicles) {
                        if (vehicle.getLaneId() == 4 && vehicle.getY() > 235)
                            count++;
                    }
                } else {
                    throw new IllegalArgumentException("laneId不存在！");
                }
            }
            int newTiming = 0;
            if (count < 10) newTiming = 10;
            else if (count < 20 && count >= 10) newTiming = (int) (count * 1.5);
            else {
                newTiming = (int) (count * 1.4);
            }

            newTiming = Math.max(newTiming, minGreenTime);
            newTiming = Math.min(newTiming, maxGreenTime);
            if (data.getLaneId() == 1 || data.getLaneId() == 3) {
                int old = signalTiming.get(1);
                if (old < newTiming) signalTiming.put(1, newTiming);
            } else {
                int old = signalTiming.get(2);
                if (old < newTiming) signalTiming.put(2, newTiming);
            }

            log(time() + " - " + deviceId + ": 本地调整车道" + data.getLaneId() +
                    "绿灯时间为 " + newTiming + "秒");
        }
    }

    private void updateTrafficLights() {
        synchronized (lightStates) {
            synchronized (lightTimers) {
                for (int i = 0; i < 2; i++) {
                    if (lightStates[i] == LightState.GREEN) {
                        if (!useFixedTiming) {
                            directionActive[i] = true;
                        }
                        if (lightTimers[i] > 0) {
                            lightTimers[i]--;
                        } else {
                            lightStates[i] = LightState.YELLOW;
                            lightTimers[i] = yellowLightDuration;
                            if (!useFixedTiming) {
                                directionActive[i] = false;
                            }
                            log(time() + " - " + deviceId + ": 车道" + (i + 1) + " 绿灯→黄灯");
                        }
                    } else if (lightStates[i] == LightState.YELLOW) {
                        if (lightTimers[i] > 0) {
                            lightTimers[i]--;
                        } else {
                            lightStates[i] = LightState.RED;
                            int otherLane = (i + 1) % 2;

                            lightStates[otherLane] = LightState.GREEN;
                            lightTimers[otherLane] = signalTiming.get(otherLane + 1);
                            lightTimers[i] = lightTimers[otherLane] + 4;

                            if (!useFixedTiming) {
                                hasVehiclePassed[otherLane] = false;
                                directionIdleTime[otherLane] = 0;
                                directionActive[otherLane] = true;
                            }

                            log(time() + " - " + deviceId + ": 车道" + (i + 1) + " 黄灯→红灯");
                            log(time() + " - " + deviceId + ": 车道" + (otherLane + 1) +
                                    " 红灯→绿灯 (" + lightTimers[otherLane] + "秒)");
                        }
                    } else {
                        int otherLane = (i + 1) % 2;
                        lightTimers[i] = lightStates[otherLane] == LightState.GREEN ?
                                lightTimers[otherLane] + 4 :
                                lightTimers[otherLane];
                    }
                }
            }
        }
    }

    private void updateVehicles() {
        synchronized (vehicles) {
            Iterator<Vehicle> iterator = vehicles.iterator();
            while (iterator.hasNext()) {
                Vehicle vehicle = iterator.next();
                vehicle.update(lightStates[vehicle.getLaneId() - 1 > 1 ? vehicle.getLaneId() - 3 : vehicle.getLaneId() - 1]);

                if (vehicle.hasLeftIntersection()) {
                    vehicle.setPreVehicle(null);
                    laneVehicles[vehicle.getLaneId()-1]-=1;
                    iterator.remove();
                }
            }
        }
    }

    public void draw(Graphics g, int baseX, int baseY) {
        synchronized (vehicles) {
            for (Vehicle vehicle : vehicles) {
                int[] rel = vehicle.draw(g, baseX, baseY,
                        lightStates[vehicle.getLaneId() - 1 > 1 ? vehicle.getLaneId() - 3 : vehicle.getLaneId() - 1]);
                switch (rel[0]) {
                    case 1:
                        position_1_next_new = rel[1] - gap_1_vehicle;
                        if (position_1_next_new > -40 * 4) position_1_next_new = -40 * 4;
                        break;
                    case 2:
                        position_2_next_new = rel[1] -gap_2_vehicle;
                        if (position_2_next_new > -40 * 4) position_2_next_new = -40 * 4;
                        break;
                    case 3:
                        position_3_next_new = rel[1] -gap_3_vehicle;
                        if (position_3_next_new > -40 * 4) position_3_next_new = -40 * 4;
                        break;
                    case 4:
                        position_4_next_new = rel[1] -gap_4_vehicle;
                        if (position_4_next_new > -40 * 4) position_4_next_new = -40 * 4;
                        break;
                }
            }
        }
    }

    private String time() {
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
    }

    private void log(String message) {
        SwingUtilities.invokeLater(() -> {
            logModel.addElement(message);
        });
    }

    private Vehicle getPreVehicle(int laneId) {
        synchronized (vehicles) {
            for (int j = vehicles.size() - 1; j >= 0; j--) {
                if (vehicles.get(j).getLaneId() == laneId) {
                    return vehicles.get(j);
                }
            }
            return null;
        }
    }

    public Map<Integer, Integer> getSignalTiming() {
        return new HashMap<>(signalTiming);
    }

    public LightState[] getLightStates() {
        synchronized (lightStates) {
            return Arrays.copyOf(lightStates, lightStates.length);
        }
    }

    public int[] getLightTimers() {
        synchronized (lightTimers) {
            return Arrays.copyOf(lightTimers, lightTimers.length);
        }
    }

    public String getDeviceId() {
        return deviceId;
    }

    public String getLocation() {
        return location;
    }

    public boolean isSchoolZone() {
        return isSchoolZone;
    }

    public boolean isUsingFixedTiming() {
        return useFixedTiming;
    }

    public TrafficData getLatestTrafficData() {
        return localDataQueue.isEmpty() ? null : localDataQueue.peek();
    }

    public List<Vehicle> getVehicles() {
        return vehicles;
    }
}